现在的大趋势是全站https,所以我也折腾了一下https,用的是let’s Encrypt的证书,然后使用nginx做https服务器,顺便也升级了一下到http2.0。

let’s Encrypt

使用非常简单,按照教程做就可以了。我这里用的是自动配置的方式,也提供高级的自定义配置。

  1. 下载安装脚本并安装
1
2
wget https://dl.eff.org/certbot-auto
chmod a+x certbot-auto
  1. 指定目录校验网站并生成证书
1
./certbot-auto certonly --webroot -w /var/www/example -d example.com -d www.example.com -w /var/www/thing -d thing.is -d m.thing.is

生成证书后注意控制台的输出,到相应的live目录中获取生成的证书。

1
2
3
4
5
README:说明文件
fullchain.pem:证书文件
privkey.pem:私钥
chain.pem:OCSP(Online Certificate Status Protocol,在线证书状态协议),用于在线验证证书
cert.pem:
  1. 配置证书到nginx服务器配置中
1
2
3
4
5
6
ssl on;                                                                                                                     
#证书文件
ssl_certificate ssl/fullchain.pem;
ssl_certificate_key ssl/privkey.pem;
# 配置支持 ocsp stapling
ssl_trusted_certificate ssl/chain.pem;

定期更新

  1. 使用更新命令

    1
    /srv/certbot-auto renew
  2. /etc/letsencrypt/live/qieguo.me目录下会生成所需的认证套件,将其复制到/alidata/server/nginx/conf/ssl/下即可

    1
    cp /etc/letsencrypt/live/qieguo.me/* /alidata/server/nginx/conf/ssl/
  3. 重启nginx

    1
    /alidata/server/nginx/sbin/nginx -s reload
  4. 将上述操作用crontab做成定时任务

    1
    2
    3
    4
    5
    6
    #!/bin/bash    

    # renew.sh
    /srv/certbot-auto renew
    cp /etc/letsencrypt/live/qieguo.me/* /alidata/server/nginx/conf/ssl/
    /alidata/server/nginx/sbin/nginx -s reload

定时任务:
crontab -e
0 0 1 */3 * /alidata/renew-https-cert.sh

NOTE:写完定时任务记得启动sudo service cron restart

https应用中的一些注意点

http请求的重定向

用户直接在浏览器地址栏输入网址时,浏览器默认会使用http进行访问;另外你的链接被别人分享的时候,也很有可能被写成http网址,
所以很多服务器都在http页面上使用30x重定向跳转来使用https协议访问:

1
2
3
4
5
6
7
8
9
10
11
server {
listen 80;
server_name qieguo.me;
rewrite ^/(.*)$ https://$host/$1 permanent;
}

server {
listen 443 ssl http2 spdy;
server_name qieguo.me;
include vhost/https.qieguo.me.inner;
}

这种模式不错,但是如果你的http页面被劫持了,那么用户根本到不了你的https页面,
也就意味着你精细准备的https根本派不上用场就被人截胡了。这时候你可以启动HSTS(HTTP Strict Transport Security)来让浏览器强制使用https访问。
HSTS可以让浏览器帮你做30x跳转,省一次HTTP请求。另外,浏览器本地替换可以保证只会发送HTTPS请求,避免被劫持。

要使用HSTS,只需要在你的HTTPS网站响应头中,加入下面这行:

strict-transport-security: max-age=16070400; includeSubDomains

includeSubDomains是可选的,用来指定是否作用于子域名。支持HSTS的浏览器遇到这个响应头,会把当前网站加入HSTS列表,
然后在max-age指定的秒数内,当前网站所有请求都会被重定向为https。即使用户主动输入http://或者不输入协议部分,都将重定向到https://地址。

但是HSTS需要浏览器先登录一次https页面之后才能生效,也就是说用户的首次登录仍然需要上述手动配置的rewrite跳转,这个环节的http访问依然有可能被劫持。如果你需要浏览器首次访问就直接强制使用https请求,那么你就需要在https://hstspreload.appspot.com登记你的域名,然后HSTS配置增加一个preload字段。如果你的网站通过审核,那么浏览器就会帮你做首次跳转了。

全站资源https化

决定要使用https之时,往往需要全站升级到https,避免出现Mixed Content的情况(在https站点中加载http资源)。因为加载的http资源如果是不安全的,那么启用https也没意义了。
另一方面,浏览器也越来越严格,Mixed Content资源在现代浏览器中可能已经加载不了了(尤其是js这种危险系数极高的资源)。
如果站点比较大,在全站往HTTPS迁移的过程中,工作量往往非常巨大,尤其是将所有资源都替换为 HTTPS 这一步,很容易产生疏漏。
这时候可以通过 upgrade-insecure-requests 这个 CSP 指令,可以让浏览器帮忙做这个转换。启用这个策略后,有两个变化:

  • 页面所有 HTTP 资源,会被替换为 HTTPS 地址再发起请求;
  • 页面所有站内链接,点击后会被替换为 HTTPS 地址再跳转;

其他一些配置

  1. 旧版本的SSL并不安全,所以我们应该启用新的TLS并选择安全的加密套件(算法)
1
2
3
4
5
6
7
8
9
10
11
# 配置只支持tls协议
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

# SSL密码套件配置(不兼容IE6 / WinXP):
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';

# 兼容(IE6 / WinXP):
ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";

# 强制指定使用服务器端的偏好
ssl_prefer_server_ciphers on;
  1. 向前保密的概念很简单:客户端和服务器协商一个可靠的密钥,并在会话结束后销毁。服务器中的RSA私钥用来签名客户端和服务器之间交换的Diffie-Hellman密钥。副主密钥从Diffie-Hellman握手中得到,并用于加密。由于副主密钥在客户端和服务器之间的连接中是明确具体的,并用于有限的时间,因此被叫作Ephemeral(短暂的)。由于有Forward Secrecy,即使攻击者持有服务器的私钥,也不能够解密过去的会话。私钥仅仅用来签名DH(Diffie-Hellman)的握手,它并没有泄漏副主密钥。Diffie-Hellman确保了副主密钥不会离开客户端和服务器,也不会被中间人截获。
    但由于我们正在使用一个2048位的证书,DHE客户端比非ephemeral客户端将使用一个更弱的密钥(1024位)交换。所以我们要单独生成一个2048位的DHE参数:
1
2
#配置支持 Perfect Forward Secrecy
ssl_dhparam ssl/dhparam.pem;
  1. OCSP一次只获取一条记录。但是副作用是,当连接到服务器的时候,OCSP请求必须发送到第三方响应者,这增加了延迟,以及失败的可能。实际上,OCSP响应者由CA操控,由于它常常不可靠,导致浏览器由于收不到适时的响应而失败。这减少了安全性,因为它允许攻击者对OCSP响应者进行DoS攻击来取消验证。解决方案是在TLS握手期间,允许服务器发送缓存的OCSP记录,这样来绕过OCSP响应者。这个技术节省了在客户端和OCSP响应者之间的一个来回,称为OCSP闭合(OCSP Stapling)。服务器只在客户端请求的时候,发送一个缓存的OCSP响应,通过对CLIENT HELLO的status_request TLS拓展来声明支持。大多数服务器都会缓存OCSP响应到48小时。在常规间隔,服务器会连接到CA的OCSP响应者来获取最新的OCSP记录。OCSP响应者的位置是从签名证书的Authority Information Access 字段来获取。

OCSP(Online Certificate Status Protocol,在线证书状态协议)是维护服务器和其它网络资源安全性的两种普遍模式之一。OCSP克服了证书注销列表(CRL)的主要缺陷:必须经常在客户端下载以确保列表的更新。当用户试图访问一个服务器时,在线证书状态协议发送一个对于证书状态信息的请求。服务器回复一个“有效”、“过期”或“未知”的响应。协议规定了服务器和客户端应用程序的通讯语法。在线证书状态协议给了用户的到期的证书一个宽限期,这样他们就可以在更新以前的一段时间内继续访问服务器。

1
2
3
4
5
6
# 配置支持 ocsp stapling
ssl_trusted_certificate ssl/chain.pem;
ssl_stapling on;
ssl_stapling_verify on;
resolver 223.5.5.5 223.6.6.6 valid=300s; #AliDNS 阿里公共DNS
resolver_timeout 15s;